package cn.xiuxius.embedded.toolwindow.flashtool;

import cn.xiuxius.embedded.toolwindow.model.*;
import com.fazecast.jSerialComm.SerialPort;

import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandlerFactory;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.*;

import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Key;
import com.intellij.ui.*;

import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.*;
import com.intellij.util.ui.*;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class FlashingToolWindow extends SimpleToolWindowPanel {

    private JPanel mainPanel;
    private List<FlashOptionItem> persistentItems = new ArrayList<>();
    private List<FlashOptionItem> advancedOptions = new ArrayList<>();
    private File hexFile = null;
    private OSProcessHandler currentProcessHandler = null;
    private LogPanel logPanel;
    private FilePanel filePanel;
    private Project project;

    public FlashingToolWindow(Project project) {
        super(true, true);
        this.project = project;
        mainPanel = this;
        initItems();
        // 创建四个主要区域
        JPanel persistentArea = createPersistentArea();
        JComponent advancedOptionsArea = createAdvancedOptionsArea();
        JComponent fileArea = createActionArea();
        JComponent logArea = createLogArea();

        // 调整布局比例 - 使用更精确的比例控制
        OnePixelSplitter mainSplitter = new OnePixelSplitter(true, 0.40f); // 35% 给上部，65% 给下部
        OnePixelSplitter fileLogSplitter = new OnePixelSplitter(true, 0.35f); // 35% 给文件区，65% 给日志区

        // 上部面板 (常驻区 + 高级选项区)
        OnePixelSplitter upperSplitter = new OnePixelSplitter(true, 0.60f); // 50% 给常驻区，50% 给高级选项区
        upperSplitter.setFirstComponent(persistentArea);
        upperSplitter.setSecondComponent(advancedOptionsArea);
        upperSplitter.setDividerPositionStrategy(Splitter.DividerPositionStrategy.KEEP_FIRST_SIZE);

        // 下部面板 (文件区 + 日志区)
        fileLogSplitter.setFirstComponent(fileArea);
        fileLogSplitter.setSecondComponent(logArea);
        fileLogSplitter.setDividerPositionStrategy(Splitter.DividerPositionStrategy.KEEP_FIRST_SIZE);

        // 主布局
        mainSplitter.setFirstComponent(upperSplitter);
        mainSplitter.setSecondComponent(fileLogSplitter);
        mainSplitter.setDividerPositionStrategy(Splitter.DividerPositionStrategy.KEEP_FIRST_SIZE);

        add(mainSplitter, BorderLayout.CENTER);
    }

    private void initItems() {
        initPersistentItems();
        initAdvancedOptions();
    }

    private void startFlash() {
        String home = System.getenv("EASY_STC_HOME");
        if (home == null || home.isEmpty()) {
            Messages.showErrorDialog(this, "未设置 EASY_STC_HOME 环境变量", "错误");
            return;
        }

        File exe = new File(home, "easystc.exe");
        if (!exe.exists()) {
            Messages.showErrorDialog(this, "easystc.exe 不存在: " + exe.getAbsolutePath(), "错误");
            return;
        }
        if (hexFile == null || !hexFile.exists()) {
            Messages.showErrorDialog(this, "HEX 文件路径无效", "错误");
            return;
        }

        // 看看有没有串口选项
        boolean hasCom = persistentItems.stream()
                .filter(item -> "com-port".equals(item.getId()))
                .anyMatch(item -> item.getOption().getCmdValue() != null && !item.getOption().getCmdValue().trim().isEmpty());

        if (!hasCom) {
            Messages.showErrorDialog(this, "请选择串口", "错误");
            return;
        }

        GeneralCommandLine commandLine = new GeneralCommandLine();
        commandLine.setExePath(exe.getAbsolutePath());
        commandLine.addParameters("ex", "flash");
        addOptionsToCommandLine(commandLine, persistentItems);
        addOptionsToCommandLine(commandLine, advancedOptions);
        commandLine.addParameter("-code");
        commandLine.addParameter(hexFile.getAbsolutePath());

        OSProcessHandler handler;
        try {
            currentProcessHandler = ProcessHandlerFactory.getInstance().createColoredProcessHandler(commandLine);
            handler = currentProcessHandler;
        } catch (ExecutionException e) {
            Messages.showErrorDialog(this, "启动烧录进程失败: " + e.getMessage(), "错误");
            return;
        }
        handler.addProcessListener(new ProcessAdapter() {

            private static final Pattern ANSI_PATTERN = Pattern.compile("\u001B\\[[;\\d]*m");

            @Override
            public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
                SwingUtilities.invokeLater(() -> logPanel.appendLog(event.getText()));
            }

            @Override
            public void processTerminated(@NotNull ProcessEvent event) {
                int exitCode = event.getExitCode();
                SwingUtilities.invokeLater(() -> {
                    if (exitCode == 0) {
                        Messages.showInfoMessage(mainPanel, "烧录成功!", "成功");
                    } else {
                        Messages.showErrorDialog(mainPanel, "烧录失败，退出码: " + exitCode, "失败");
                    }
                });
                currentProcessHandler = null; // 清空
            }
        });

        handler.startNotify();
    }

    private void addOptionsToCommandLine(GeneralCommandLine commandLine, List<FlashOptionItem> items) {
        for (FlashOptionItem item : items) {
            if (item.getOption().isValid()) {
                if (item.getOption().isDefault()) {
                    continue;
                }
                commandLine.addParameter(item.getOption().getCmdFlag());
                if (!item.getOption().isOnlyFlag()) {
                    commandLine.addParameter(item.getOption().getCmdValue());
                }
            }
        }
    }


    private void stopFlash() {
        if (currentProcessHandler != null && !currentProcessHandler.isProcessTerminated()) {
            currentProcessHandler.destroyProcess();
            logPanel.appendLog("[INFO]  烧录已被用户取消。\n");
        }
    }

    private void cleanFile() {
        filePanel.clear();
        hexFile = null;
    }

    public void initPersistentItems() {
        persistentItems.addAll(List.of(
                new FlashOptionItem(OptionUIType.COMBO_BOX_WITH_REFRESH, "com-port", "串口",
                        new StringCommandOption("-port", "auto", false)
                                .options(() -> Arrays.stream(SerialPort.getCommPorts()).map(SerialPort::getSystemPortName).toList())
                        , true),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "device-type", "设备类型",
                        new StringCommandOption("-protocol", "auto", false)
                                .options(() -> List.of("stc8g", "stc8d", "stc8", "stc15a", "stc15", "stc12a", "stc12b", "stc12", "stc89", "usb15", "auto"))
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX_WITH_CUSTOM, "transfer-baud", "波特率",
                        new IntegerCommandOption("-b", 115200)
                                .validator(s -> s.matches("\\d+"))
                                .options(() -> List.of(9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600))
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX_WITH_CUSTOM, "handshake-baud", "握手",
                        new IntegerCommandOption("--handshake", 2400).options(() -> List.of(2400))
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX_WITH_CUSTOM, "user-program-eeprom", "用户EEPROM大小",
                        new FloatCommandOption("--user-program-eeprom", 0.5f)
                                .validator(s -> s.matches("\\d+(\\.\\d+)?") && Float.parseFloat(s) > 0 && Float.parseFloat(s) <= 64)
                                .options(() -> // 保留一位小数
                                        Stream.iterate(0.5f, d -> d <= 64.0f, d -> d * 2.0f)
                                                .map(d -> (float) (Math.round(d * 10) / 10.0))
                                                .collect(Collectors.toList()))
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.CHECK_BOX, "eeprom-erase", "下一编程周期擦除EEPROM",
                        new BooleanCommandOption("--eeprom-erase", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.CHECK_BOX, "erase-only", "仅擦除Flash",
                        new BooleanCommandOption("--erase-only", false, true)
                        , false)
        ));
    }

    public void initAdvancedOptions() {
        advancedOptions.addAll(List.of(
                new FlashOptionItem(OptionUIType.COMBO_BOX, "trim-frequency", "RC振荡器频率（kHz）",
                        new IntegerCommandOption("--trim", 0)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "auto-reset", "自动复位（DTR）",
                        new BooleanCommandOption("--autoreset", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "reset-pin", "自动复位引脚",
                        new StringCommandOption("--resetpin", "dtr", false)
                                .options(() -> List.of("dtr", "rts"))
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "reset-cmd", "自定义电源复位命令",
                        new StringCommandOption("--resetcmd", "", false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "debug-output", "调试输出",
                        new BooleanCommandOption("--debug", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "por-reset-delay", "上电复位延迟",
                        new StringCommandOption("--por-reset-delay", "DEFAULT", false)
                                .options(() -> List.of("SHORT", "LONG", "DEFAULT"))
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "cpu-core-voltage", "CPU核心电压",
                        new StringCommandOption("--cpu-core-voltage", "DEFAULT", false)
                                .options(() -> List.of("LOW", "MID", "HIGH", "DEFAULT"))
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "watchdog-por", "上电复位启用看门狗",
                        new BooleanCommandOption("--watchdog-por", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "watchdog-prescale", "看门狗分频",
                        new IntegerCommandOption("--watchdog-prescale", 256)
                                .options(() -> List.of(2, 4, 8, 16, 32, 64, 128, 256))
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "bsl-pindetect", "引脚低电平时进入BSL",
                        new BooleanCommandOption("--bsl-pindetect", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "reset-pin-enabled", "GPIO复位引脚启用",
                        new BooleanCommandOption("--reset-pin", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "ale-enabled", "启用ALE引脚（STC89）",
                        new BooleanCommandOption("--ale", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "uart1-remap", "UART1重映射至UART2",
                        new BooleanCommandOption("--uart1-remap", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "epwm-open-drain", "EPWM引脚开漏模式",
                        new BooleanCommandOption("--epwm-open-drain", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "clock-gain", "外部晶振增益",
                        new BooleanCommandOption("--clock-gain", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "xram-enabled", "启用内部XRAM（STC89）",
                        new BooleanCommandOption("--xram", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "clock-source", "时钟源选择",
                        new StringCommandOption("--clock-source", "DEFAULT", false)
                                .options(() -> List.of("INTERNAL", "EXTERNAL", "DEFAULT"))
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "oscillator-delay", "振荡器稳定延迟",
                        new IntegerCommandOption("--oscillator-delay", 8192)
                                .options(() -> List.of(4096, 8192, 16384, 32768))
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "watchdog-idle-stop", "空闲模式停止看门狗",
                        new BooleanCommandOption("--watchdog-idle-stop", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "lvd-threshold", "低电压检测阈值",
                        new IntegerCommandOption("--lvd-threshold", 0)
                                .options(() -> List.of(0, 1, 2, 3, 4, 5, 6, 7))
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "eeprom-lvd-inhibit", "低电压忽略EEPROM写入",
                        new BooleanCommandOption("--eeprom-lvd-inhibit", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "rstout-por-state", "POR后RSTOUT状态",
                        new StringCommandOption("--rstout-por", "DEFAULT", false)
                                .options(() -> List.of("LOW", "HIGH", "DEFAULT"))
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "uart2-passthrough", "UART1透传至UART2",
                        new BooleanCommandOption("--uart2-passthrough", true, false)
                                .advanced()
                        , false),
                new FlashOptionItem(OptionUIType.COMBO_BOX, "uart2-pin-mode", "UART2引脚输出模式",
                        new StringCommandOption("--uart2-pin-mode", "DEFAULT", false)
                                .options(() -> List.of("PUSH_PULL", "NORMAL", "DEFAULT"))
                                .advanced()
                        , false)
        ));
    }

    // ==================== Persistent Area ====================
    private JPanel createPersistentArea() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(JBUI.Borders.empty(5));
        panel.setMinimumSize(new Dimension(100, 150));
        PersistentPanel persistentPanel = new PersistentPanel(persistentItems);
        panel.add(persistentPanel, BorderLayout.CENTER);
        return panel;
    }

    // ==================== Advanced Options Area ====================
    private JComponent createAdvancedOptionsArea() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(JBUI.Borders.empty(5));

        AdvancedOptionsPanel advancedOptionsPanel = new AdvancedOptionsPanel(advancedOptions);
        // 使用滚动面板包装高级选项区
        JScrollPane scrollPane = new JBScrollPane(advancedOptionsPanel);
        scrollPane.setBorder(JBUI.Borders.empty());
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        panel.add(scrollPane, BorderLayout.CENTER);
        return panel;
    }

    // ==================== Action Area ====================
    private JComponent createActionArea() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(JBUI.Borders.empty(5));
        // Example file operations
        List<ActionType> actionTypes = Arrays.asList(
                new ActionType("flash", "烧录", e -> {
                    startFlash();
                }),
                new ActionType("stop", "停止", e -> {
                    stopFlash();
                }),
                new ActionType("clean", "清除", e -> {
                    cleanFile();
                })
        );
        this.filePanel = new FilePanel(file -> hexFile = file);
        ActionBtnPanel btnPanel = new ActionBtnPanel(actionTypes);
        JScrollPane scrollPane = new JBScrollPane(btnPanel,
                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setPreferredSize(new Dimension(150, 1000));
        panel.add(filePanel, BorderLayout.CENTER);
        panel.add(scrollPane, BorderLayout.EAST);
        return panel;
    }

    // ==================== Log Area ====================
    private JComponent createLogArea() {
        logPanel = new LogPanel(project);
        return logPanel;
    }

    // ==================== Data Classes ====================

    public static class ActionType {
        final String id;
        @Getter
        final String displayName;
        final ActionListener actionListener;
        @Getter
        JButton jButton;

        ActionType(String id, String displayName, ActionListener actionListener) {
            this.id = id;
            this.displayName = displayName;
            this.actionListener = actionListener;
            jButton = new JButton(displayName);
            jButton.addActionListener(actionListener);
        }

    }

    // ==================== UI Components ====================

    public static class PersistentPanel extends JPanel {
        private final Map<String, JComponent> itemsMap = new HashMap<>();
        private static final String CUSTOM_ITEM_PREFIX = "自定义: ";
        private final List<FlashOptionItem> items;
        private final JPanel contentPanel = new JPanel();

        public PersistentPanel(List<FlashOptionItem> items) {
            super(new BorderLayout());
            this.items = items;

            contentPanel.setBorder(JBUI.Borders.empty(5));
            add(contentPanel, BorderLayout.CENTER);

            rebuildLayout();

            this.addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    rebuildLayout();
                }
            });
        }

        private void rebuildLayout() {
            contentPanel.removeAll();

            int panelWidth = this.getWidth();
            if (panelWidth <= 0) panelWidth = 600; // 防止首次宽度为0

            int minItemWidth = 180; // 每个item最小宽度，调节这个控制换列阈值
            int cols = Math.min(4, Math.max(2, panelWidth / minItemWidth));

            int rows = (int) Math.ceil(items.size() / (double) cols);

            contentPanel.setLayout(new GridLayout(rows, cols, 10, 10));

            for (FlashOptionItem item : items) {
                JPanel itemPanel = createItemPanel(item);
                contentPanel.add(itemPanel);
            }

            // 填充空白单元格，保证网格完整
            int totalCells = rows * cols;
            int blanks = totalCells - items.size();
            for (int i = 0; i < blanks; i++) {
                JPanel filler = new JPanel();
                filler.setOpaque(false);
                contentPanel.add(filler);
            }

            contentPanel.revalidate();
            contentPanel.repaint();
        }

        private void bindValueToComboBox(FlashOptionItem item, JComboBox<Selectable> comboBox) {
            comboBox.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Selectable selectedItem = (Selectable) comboBox.getSelectedItem();
                    item.getOption().setCmdValue(selectedItem == null ? item.getOption().getDefaultValue() : selectedItem.getValue());

                }
            });
        }

        private void bindValueToCheckBox(FlashOptionItem item, JCheckBox checkBox) {
            // 初始值绑定
            item.getOption().setCmdValue(Boolean.toString(checkBox.isSelected()));
            // 添加监听器：状态变动时同步更新
            checkBox.addActionListener(e -> {
                boolean selected = checkBox.isSelected();
                item.getOption().setCmdValue(Boolean.toString(selected));
            });
        }

        private JPanel createItemPanel(FlashOptionItem item) {
            JPanel itemPanel = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = JBUI.insets(2); // 控件间距
            gbc.gridy = 0;

            switch (item.getType()) {
                case COMBO_BOX:
                    JLabel label = new JLabel(item.getDisplayName() + ": ");
                    JComboBox<Selectable> comboBox = new ComboBox<>();
                    for (Selectable option : item.getOption().getSelectables()) {
                        comboBox.addItem(option);
                        comboBox.setName(option.getValue());
                    }
                    if (item.getOption().getDefaultValue() != null) {
                        comboBox.setSelectedItem(item.getOption().getDefaultValue());
                        item.getOption().setCmdValue(item.getOption().getDefaultValue()); // 初始化赋值
                    }
                    bindValueToComboBox(item, comboBox);
                    itemsMap.put(item.getId(), comboBox);

                    gbc.gridx = 0;
                    gbc.weightx = 0;
                    gbc.fill = GridBagConstraints.NONE;
                    itemPanel.add(label, gbc);

                    gbc.gridx = 1;
                    gbc.weightx = 1;
                    gbc.fill = GridBagConstraints.HORIZONTAL;
                    itemPanel.add(comboBox, gbc);
                    break;

                case COMBO_BOX_WITH_CUSTOM:
                    JLabel customLabel = new JLabel(item.getDisplayName() + ": ");
                    JComboBox<Selectable> customComboBox = new ComboBox<>();
                    for (Selectable option : item.getOption().getSelectables()) {
                        customComboBox.addItem(option);
                        customComboBox.setName(option.getValue());
                    }
                    customComboBox.addItem(new Selectable("自定义").setType(Selectable.Type.CUSTOM));
                    if (item.getOption().getDefaultValue() != null) {
                        customComboBox.setSelectedItem(item.getOption().getDefaultValue());
                        item.getOption().setCmdValue(item.getOption().getDefaultValue());
                    }
                    bindValueToComboBox(item, customComboBox);

                    customComboBox.addActionListener(e -> {
                        Selectable select = (Selectable) customComboBox.getSelectedItem();
                        if (select != null && e.getSource() == customComboBox &&
                                Selectable.Type.CUSTOM.equals(select.getType())) {
                            CustomParamDialog dialog = new CustomParamDialog(item);
                            if (dialog.showAndGet()) {
                                String customValue = dialog.getEnteredValue();
                                if (customValue != null && !customValue.isEmpty()) {
                                    removeExistingCustomItem(customComboBox);
                                    Selectable newCustomItem = new Selectable("自定义" + ": " + customValue).setType(Selectable.Type.CUSTOM);
                                    customComboBox.insertItemAt(newCustomItem, customComboBox.getItemCount());
                                    customComboBox.setSelectedItem(newCustomItem);
                                    item.getOption().setCmdValue(customValue); // 手动设置值
                                }
                            } else {
                                if (customComboBox.getItemCount() > 1) {
                                    customComboBox.setSelectedIndex(0);
                                }
                            }
                        } else {
                            Selectable selectedItem = (Selectable) customComboBox.getSelectedItem();
                            item.getOption().setCmdValue(selectedItem == null ? item.getOption().getDefaultValue() : selectedItem.getValue()); // 普通切换也更新值
                        }
                    });

                    itemsMap.put(item.getId(), customComboBox);

                    gbc.gridx = 0;
                    gbc.weightx = 0;
                    gbc.fill = GridBagConstraints.NONE;
                    itemPanel.add(customLabel, gbc);

                    gbc.gridx = 1;
                    gbc.weightx = 1;
                    gbc.fill = GridBagConstraints.HORIZONTAL;
                    itemPanel.add(customComboBox, gbc);
                    break;

                case CHECK_BOX:
                    JCheckBox checkBox = new JCheckBox(item.getDisplayName());
                    checkBox.setPreferredSize(new Dimension(150, 25));
                    item.getOption().setCmdValue(Boolean.toString(checkBox.isSelected()));
                    checkBox.addActionListener(e -> item.getOption().setCmdValue(Boolean.toString(checkBox.isSelected())));
                    itemsMap.put(item.getId(), checkBox);
                    bindValueToCheckBox(item, checkBox);
                    if (Boolean.parseBoolean(item.getOption().getDefaultValue())) {
                        checkBox.doClick();
                    }
                    gbc.gridx = 0;
                    gbc.gridwidth = 2;
                    gbc.weightx = 1;
                    gbc.fill = GridBagConstraints.HORIZONTAL;
                    itemPanel.add(checkBox, gbc);
                    break;

                case CUSTOM_BUTTON:
                    JButton button = new JButton(item.getDisplayName());
                    button.addActionListener(e -> new CustomParamDialog(item).show());
                    itemsMap.put(item.getId(), button);

                    gbc.gridx = 0;
                    gbc.gridwidth = 2;
                    gbc.weightx = 1;
                    gbc.fill = GridBagConstraints.HORIZONTAL;
                    itemPanel.add(button, gbc);
                    break;

                case COMBO_BOX_WITH_REFRESH:
                    JLabel refreshLabel = new JLabel(item.getDisplayName() + ": ");
                    JComboBox<Selectable> refreshComboBox = new ComboBox<>();
                    refreshComboBox.setModel(new DefaultComboBoxModel<>(item.getOption().getSelectables().toArray(new Selectable[0])));
                    if (item.getOption().getDefaultValue() != null) {
                        refreshComboBox.setSelectedItem(item.getOption().getDefaultValue());
                        item.getOption().setCmdValue(item.getOption().getDefaultValue());
                    }
                    bindValueToComboBox(item, refreshComboBox);
                    JButton refreshButton = new JButton("刷新");
                    refreshButton.addActionListener(e -> {
                        refreshButton.setEnabled(false);
                        refreshButton.setText("刷新中...");
                        new SwingWorker<List<Selectable>, Void>() {
                            @Override
                            protected List<Selectable> doInBackground() {
                                return item.getOption().getSelectables();
                            }

                            @Override
                            protected void done() {
                                try {
                                    List<Selectable> newItems = get();
                                    refreshComboBox.setModel(
                                            new DefaultComboBoxModel<>(newItems.toArray(new Selectable[0]))
                                    );
                                    if (!newItems.isEmpty()) {
                                        refreshComboBox.setSelectedIndex(0);
                                        item.getOption().setCmdValue(newItems.getFirst().getValue()); // 更新选中值
                                    }
                                } catch (Exception ignored) {
                                } finally {
                                    refreshButton.setText("刷新");
                                    refreshButton.setEnabled(true);
                                }
                            }
                        }.execute();
                    });
                    refreshButton.doClick();
                    itemsMap.put(item.getId(), refreshComboBox);

                    gbc.gridx = 0;
                    gbc.weightx = 0;
                    gbc.fill = GridBagConstraints.NONE;
                    itemPanel.add(refreshLabel, gbc);

                    gbc.gridx = 1;
                    gbc.weightx = 1;
                    gbc.fill = GridBagConstraints.HORIZONTAL;
                    itemPanel.add(refreshComboBox, gbc);

                    gbc.gridx = 2;
                    gbc.weightx = 0;
                    gbc.fill = GridBagConstraints.NONE;
                    itemPanel.add(refreshButton, gbc);
                    break;
            }

            return itemPanel;
        }

        private void updateComboBoxItems(JComboBox<String> comboBox, List<String> items) {
            comboBox.removeAllItems();
            for (String item : items) {
                comboBox.addItem(item);
            }
        }

        private void removeExistingCustomItem(JComboBox<Selectable> comboBox) {
            for (int i = 0; i < comboBox.getItemCount(); i++) {
                Selectable item = comboBox.getItemAt(i);
                if (Selectable.Type.CUSTOM == item.getType()) {
                    comboBox.removeItemAt(i);
                    break;
                }
            }
        }

        static class CustomParamDialog extends DialogWrapper {
            private final JBTextField textField = new JBTextField();
            private final FlashOptionItem item;

            CustomParamDialog(FlashOptionItem item) {
                super(true);
                this.item = item;
                setTitle("自定义 " + item.getDisplayName());
                init();
            }

            @Override
            protected void init() {
                super.init();
                setModal(true);
                setResizable(false);
            }

            @Override
            protected JComponent createCenterPanel() {
                JPanel panel = new JPanel(new BorderLayout());
                panel.add(new JLabel("请输入 " + item.getDisplayName() + ":"), BorderLayout.WEST);
                panel.add(textField, BorderLayout.CENTER);
                textField.setPreferredSize(new Dimension(200, 30));
                return panel;
            }

            @Override
            protected ValidationInfo doValidate() {
                if (item.getOption().isValidValue(textField.getText())) {
                    return null;
                } else {
                    return new ValidationInfo("请输入有效的 " + item.getDisplayName(), textField);
                }
            }

            @Override
            protected void doOKAction() {
                super.doOKAction();
            }

            public String getEnteredValue() {
                return textField.getText();
            }
        }
    }

    // Advanced Options Panel
    public static class AdvancedOptionsPanel extends JPanel {
        private final JComboBox<FlashOptionItem> dropdown;
        private final JButton addButton;
        private final JPanel selectedPanel;
        private final List<FlashOptionItem> availableOptions;

        AdvancedOptionsPanel(List<FlashOptionItem> availableOptions) {
            super(new BorderLayout());
            this.availableOptions = new ArrayList<>(availableOptions);

            // 顶部选择面板 - 固定高度，不参与滚动
            JPanel topPanel = new JPanel(new BorderLayout());
            topPanel.setBorder(JBUI.Borders.empty(5));
            topPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE, 40));

            JPanel controlPanel = new JPanel();
            controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS));

            dropdown = new ComboBox<>(availableOptions.toArray(new FlashOptionItem[0]));
            dropdown.setRenderer(new ListCellRendererWrapper<>() {
                @Override
                public void customize(JList list, FlashOptionItem value, int index, boolean selected, boolean hasFocus) {
                    if (value != null) {
                        setText(value.getDisplayName());
                    }
                }
            });

            addButton = new JButton("添加");
            addButton.addActionListener(e -> addSelectedOption());

            controlPanel.add(new JLabel("添加高级选项: "));
            controlPanel.add(Box.createHorizontalStrut(10));
            controlPanel.add(dropdown);
            controlPanel.add(Box.createHorizontalStrut(10));
            controlPanel.add(addButton);
            controlPanel.add(Box.createHorizontalGlue());

            topPanel.add(controlPanel, BorderLayout.CENTER);

            // 已选选项面板 - 可滚动区域
            selectedPanel = new JPanel();
            selectedPanel.setLayout(new BoxLayout(selectedPanel, BoxLayout.Y_AXIS));

            // 使用包装面板确保滚动区域正确
            JPanel scrollContent = new JPanel(new BorderLayout());
            scrollContent.add(selectedPanel, BorderLayout.NORTH);

            JBScrollPane scrollPane = new JBScrollPane(scrollContent);
            scrollPane.setBorder(JBUI.Borders.empty());
            scrollPane.setViewportBorder(JBUI.Borders.empty());
            scrollPane.getVerticalScrollBar().setUnitIncrement(10);
            scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

            // 主布局
            add(topPanel, BorderLayout.NORTH);
            add(scrollPane, BorderLayout.CENTER);
        }

        private void addSelectedOption() {
            FlashOptionItem option = (FlashOptionItem) dropdown.getSelectedItem();
            if (option == null || !availableOptions.contains(option)) return;

            JPanel row = new JPanel();
            row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
            row.setMaximumSize(new Dimension(Integer.MAX_VALUE, 40));
            row.setAlignmentX(Component.LEFT_ALIGNMENT);
            row.setBorder(JBUI.Borders.empty(5));

            JLabel label = new JLabel(option.getDisplayName() + ": ");
            JComponent valueField;

            if (option.getOption().getSelectables() != null && !option.getOption().getSelectables().isEmpty()) {
                JComboBox<Selectable> comboBox = new ComboBox<>();
                for (Selectable opt : option.getOption().getSelectables()) {
                    comboBox.addItem(opt);
                    comboBox.setName(opt.getValue());
                }
                if (option.getOption().getDefaultValue() != null) {
                    comboBox.setSelectedItem(option.getOption().getDefaultValue());
                }
                valueField = comboBox;
                bindValueToComboBox(option, comboBox);
            } else {
                JBTextField textField = new JBTextField();
                if (option.getOption().getDefaultValue() != null) {
                    textField.setText(option.getOption().getDefaultValue());
                }
                valueField = textField;
                textField.getDocument().addDocumentListener(new DocumentAdapter() {
                    @Override
                    protected void textChanged(@NotNull DocumentEvent documentEvent) {
                        try {
                            option.getOption().setCmdValue(textField.getText());
                        } catch (Exception ex) {
                            log.error("Error setting option value: " + ex.getMessage(), ex);
                        }
                    }
                });
            }

            JButton removeButton = new JButton("删除");
            removeButton.addActionListener(e -> {
                selectedPanel.remove(row);
                selectedPanel.revalidate();
                selectedPanel.repaint();
                if (!availableOptions.contains(option)) {
                    availableOptions.add(option);
                    dropdown.addItem(option);
                }
            });

            label.setPreferredSize(new Dimension(150, 30));
            valueField.setMaximumSize(new Dimension(200, 30));
            valueField.setPreferredSize(new Dimension(200, 30));
            removeButton.setPreferredSize(new Dimension(80, 30));

            row.add(Box.createHorizontalStrut(10));
            row.add(label);
            row.add(Box.createHorizontalStrut(10));
            row.add(valueField);
            row.add(Box.createHorizontalGlue());
            row.add(removeButton);
            row.add(Box.createHorizontalStrut(10));

            selectedPanel.add(row);
            selectedPanel.add(Box.createVerticalStrut(5));

            availableOptions.remove(option);
            dropdown.removeItem(option);

            selectedPanel.revalidate();
            selectedPanel.repaint();

            // 确保滚动到新添加的选项
            JViewport viewport = ((JBScrollPane) getComponent(1)).getViewport();
            viewport.setViewPosition(new Point(0, selectedPanel.getHeight()));
        }

        private void bindValueToComboBox(FlashOptionItem item, JComboBox<Selectable> comboBox) {
            Selectable selectedItem = (Selectable) comboBox.getSelectedItem();
            comboBox.addActionListener(e -> item.getOption().setCmdValue(selectedItem == null ? item.getOption().getDefaultValue() : selectedItem.getValue()));
        }
    }

    public static class ActionBtnPanel extends JPanel {
        public ActionBtnPanel(List<ActionType> operations) {
            super();
            setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
            setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); // 内边距，可根据需要调整
            for (ActionType op : operations) {
                JButton btn = op.getJButton();
                btn.setAlignmentX(Component.CENTER_ALIGNMENT); // 居中显示
                btn.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30)); // 最大宽度撑满，高度30px
                add(btn);
                add(Box.createVerticalStrut(5)); // 按钮间垂直间距
            }

            add(Box.createVerticalGlue()); // 把剩余空间填满，按钮在顶部排列
        }
    }

    // File Panel
    public static class FilePanel extends JPanel {
        private final JBTextField hexFilePathField = new JBTextField();
        private final JBLabel fileSelectLabel = new JBLabel("拖拽 HEX 文件或双击选择");
        private Consumer<File> onFileSelected;

        public FilePanel(Consumer<File> onFileSelected) {
            super(new BorderLayout());
            this.onFileSelected = onFileSelected;
            hexFilePathField.setEditable(false);
            hexFilePathField.setBackground(JBColor.PanelBackground);
            hexFilePathField.setBorder(JBUI.Borders.empty(4, 6));
            fileSelectLabel.setFont(JBUI.Fonts.label().deriveFont(Font.BOLD, 12f));
            fileSelectLabel.setHorizontalAlignment(SwingConstants.CENTER);
            fileSelectLabel.setFont(JBUI.Fonts.label().deriveFont(Font.BOLD, 12f));
            fileSelectLabel.setFont(JBUI.Fonts.label().deriveFont(Font.BOLD, 12f));
            fileSelectLabel.setHorizontalAlignment(SwingConstants.CENTER);
            super.setPreferredSize(JBUI.size(300, 100));
            super.add(fileSelectLabel, BorderLayout.CENTER);
            super.setBackground(JBColor.PanelBackground);
            super.setBorder(JBUI.Borders.customLine(JBColor.border(), 1));
            setupDragDrop();
            setupDoubleClick();
        }

        private void showBalloon(JComponent target, MessageType type) {
            JBPopupFactory.getInstance()
                    .createHtmlTextBalloonBuilder("请选择 .hex 或 .bin 文件", type, null)
                    .setFadeoutTime(3000)
                    .createBalloon()
                    .show(RelativePoint.getCenterOf(target), Balloon.Position.above);
        }

        private void setupDragDrop() {
            new DropTarget(this, new DropTargetAdapter() {
                final JComponent area = FilePanel.this;

                @Override
                public void drop(DropTargetDropEvent dtde) {
                    try {
                        dtde.acceptDrop(DnDConstants.ACTION_COPY);
                        List<File> files = (List<File>) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
                        for (File file : files) {
                            if (file.getName().toLowerCase().endsWith(".hex")) {
                                setHexPath(file.getAbsolutePath());
                                return;
                            }
                            showBalloon(area, MessageType.WARNING);
                        }
                    } catch (Exception ignored) {
                    }
                }
            });
        }

        private void setupDoubleClick() {
            addMouseListener(new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) {
                        chooseHexFile();
                    }
                }
            });
        }

        private void chooseHexFile() {
            FileDialog dialog = new FileDialog((Frame) null, "选择 HEX 文件", FileDialog.LOAD);
            dialog.setFilenameFilter((dir, name) -> name.toLowerCase().endsWith(".hex"));
            dialog.setVisible(true);
            if (dialog.getFile() != null) {
                setHexPath(new File(dialog.getDirectory(), dialog.getFile()).getAbsolutePath());
            }
        }

        private void setHexPath(String path) {
            hexFilePathField.setText(path);
            fileSelectLabel.setText("HEX 文件已选: " + new File(path).getName());
            onFileSelected.accept(new File(path));
        }

        public void clear() {
            hexFilePathField.setText("");
            fileSelectLabel.setText("拖拽 HEX 文件或双击选择");
        }
    }

    // Log Panel
    public static class LogPanel extends JPanel {

        private final ConsoleView consoleView;
        private float fontSize = 12f; // 默认字体大小

        public LogPanel(Project project) {
            super(new BorderLayout());
            setBorder(BorderFactory.createTitledBorder(
                    JBUI.Borders.customLine(JBColor.border(), 1)
            ));

            consoleView = new ConsoleViewImpl(project, false);
            JComponent consoleComponent = consoleView.getComponent();

            // 设置初始字体大小
            setConsoleFontSize(fontSize);

            // 顶部操作栏
            JPanel headerPanel = new JPanel(new BorderLayout());
            JLabel title = new JLabel("日志输出");
            title.setFont(title.getFont().deriveFont(Font.BOLD));
            JButton clearButton = new JButton("清空");
            clearButton.setMargin(JBUI.insets(2, 8));
            clearButton.addActionListener(e -> consoleView.clear());

            JPanel right = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
            right.setOpaque(false);
            right.add(clearButton);

            headerPanel.add(title, BorderLayout.WEST);
            headerPanel.add(right, BorderLayout.EAST);

            add(headerPanel, BorderLayout.NORTH);
            add(consoleComponent, BorderLayout.CENTER);

            // Ctrl + 滚轮缩放字体
            consoleComponent.addMouseWheelListener(e -> {
                if (e.isControlDown()) {
                    int rotation = e.getWheelRotation();
                    fontSize -= rotation;
                    if (fontSize < 8f) fontSize = 8f;
                    if (fontSize > 48f) fontSize = 48f;
                    setConsoleFontSize(fontSize);
                    e.consume();
                }
            });
        }

        private void setConsoleFontSize(float size) {
            Font baseFont = consoleView.getComponent().getFont();
            Font newFont = baseFont.deriveFont(size);
            consoleView.getComponent().setFont(newFont);
        }

        private void appendColoredLog(String message) {
            if (message.contains("ERROR")) {
                consoleView.print(message , ConsoleViewContentType.ERROR_OUTPUT);
            } else if (message.contains("WARN")) {
                consoleView.print(message, ConsoleViewContentType.LOG_WARNING_OUTPUT);
            } else {
                consoleView.print(message, ConsoleViewContentType.LOG_INFO_OUTPUT);
            }
        }

        public void appendLog(String message) {
            appendColoredLog(message);
        }
    }
}